package com.soo.hoo.core.model;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.soo.hoo.core.exception.GlobalException;
import io.swagger.v3.oas.annotations.media.Schema;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 分页对象。
 * 每个属性有两个setter，一个是实现IPage的，另一个是自己写的常规setter。
 * 因为使用了FastJson作为消息转换器的原因，只实现IPage定义的setter不能正常反序列化，接受不到前端传递过来的参数
 * 使用lombok的@Data也不行
 * </p>
 *
 * @author 杨燚
 * @since 2022/04/17
 */
@Schema(name = "Page",title = "分页",description = "分页")
public class Page<T> implements IPage<T> {

    /**
     * 当前页
     */
    @Schema(name = "current", description = "当前页")
    private Long current;

    /**
     * 每页条数
     */
    @Schema(description = "每页条数")
    private Long size;

    /**
     * 总条数
     */
    @Schema(description = "总条数")
    private Long total;


    /**
     * 总页数
     */
    @Schema(description = "总页数")
    private Long pages;

    /**
     * 匹配项，key：匹配项的名称，字段名，value：匹配项的值，是否排序
     */
    @Schema(description = "匹配项")
    private List<MatchItem> matchItems;

    /**
     * 排序字段
     */
    @Schema(description = "排序字段")
    private List<OrderItem> orderItems;

    /**
     * 数据列表
     */
    @Schema(description = "数据列表")
    private List<T> records;

    public Page() {
        this.current = 1L;
        this.size = 10L;
        this.matchItems = Collections.emptyList();
        this.orderItems = Collections.emptyList();
        this.records = Collections.emptyList();
    }

    public Page(Long current, Long size, Long total, Long pages, List<T> records) {
        this.current = current;
        this.size = size;
        this.total = total;
        this.pages = pages;
        this.records = records;
    }

    public Page(Long current, Long size) {
        this.current = current;
        this.size = size;
        this.total = 0L;
        this.pages = 0L;
        this.records = new ArrayList<>(0);
    }

    @Override
    public long getCurrent() {
        return this.current;
    }

    public void setCurrent(Long current) {
        this.current = current;
    }

    @Override
    public IPage<T> setCurrent(long current) {
        this.current = current;
        return this;
    }

    @Override
    public long getSize() {
        return this.size;
    }

    public void setSize(Long size) {
        this.size = size;
    }

    @Override
    public IPage<T> setSize(long size) {
        this.size = size;
        return this;
    }

    @Override
    public long getTotal() {
        return this.total;
    }

    public void setTotal(Long total) {
        this.total = total;
    }

    @Override
    public IPage<T> setTotal(long total) {
        this.total = total;
        return this;
    }

    @Override
    public long getPages() {
        return IPage.super.getPages();
    }

    public void setPages(Long pages) {
        this.pages = pages;
    }

    @Override
    public List<T> getRecords() {
        return this.records;
    }

    @Override
    public IPage<T> setRecords(List<T> records) {
        this.records = records;
        return this;
    }

    public List<MatchItem> getMatchItems() {
        return matchItems;
    }

    public void setMatchItems(List<MatchItem> matchItems) {
        this.matchItems = matchItems;
    }

    public List<OrderItem> getOrderItems() {
        return orderItems;
    }

    public void setOrderItems(List<OrderItem> orderItems) {
        this.orderItems = orderItems;
    }

    @Override
    public List<OrderItem> orders() {
        if (CollUtil.isNotEmpty(this.orderItems)) {
            return this.orderItems.stream()
                    .map(e -> new OrderItem(StrUtil.toUnderlineCase(e.getColumn()), e.isAsc()))
                    .collect(Collectors.toList());
        }
        return this.orderItems;
    }

    /**
     * 构建查询条件
     *
     * @return 查询条件
     */
    public Wrapper<T> wrapper() {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        if (CollUtil.isNotEmpty(this.matchItems)) {
            for (MatchItem matchItem : this.matchItems) {
                String fieldName = StrUtil.toUnderlineCase(matchItem.getColumn());
                switch ((matchItem.getOperator())) {
                    case NE -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.ne(fieldName, matchItem.getValue()));
                        } else {
                            queryWrapper.or(qw -> qw.ne(fieldName, matchItem.getValue()));
                        }
                    }
                    case GT -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.gt(fieldName, matchItem.getValue()));
                        } else {
                            queryWrapper.or(qw -> qw.gt(fieldName, matchItem.getValue()));
                        }
                    }
                    case GE -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.ge(fieldName, matchItem.getValue()));
                        } else {
                            queryWrapper.or(qw -> qw.ge(fieldName, matchItem.getValue()));
                        }
                    }
                    case LT -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.lt(fieldName, matchItem.getValue()));
                        } else {
                            queryWrapper.or(qw -> qw.lt(fieldName, matchItem.getValue()));
                        }
                    }
                    case LE -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.le(fieldName, matchItem.getValue()));
                        } else {
                            queryWrapper.or(qw -> qw.le(fieldName, matchItem.getValue()));
                        }
                    }
                    case LIKE -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.like(fieldName, matchItem.getValue()));
                        } else {
                            queryWrapper.or(qw -> qw.like(fieldName, matchItem.getValue()));
                        }
                    }
                    case NOT_LIKE -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.notLike(fieldName, matchItem.getValue()));
                        } else {
                            queryWrapper.or(qw -> qw.notLike(fieldName, matchItem.getValue()));
                        }
                    }
                    case IN -> {
                        if (matchItem.getValue() instanceof List) {
                            if (matchItem.getAnd()) {
                                queryWrapper.and(qw -> qw.in(fieldName, Convert.toList(matchItem.getValue())));
                            } else {
                                queryWrapper.or(qw -> qw.in(fieldName, Convert.toList(matchItem.getValue())));
                            }
                        } else {
                            throw new GlobalException("使用in子句时匹配值必须为数组");
                        }
                    }
                    case NOT_IN -> {
                        if (matchItem.getValue() instanceof List) {
                            if (matchItem.getAnd()) {
                                queryWrapper.and(qw -> qw.notIn(fieldName, Convert.toList(matchItem.getValue())));
                            } else {
                                queryWrapper.or(qw -> qw.notIn(fieldName, Convert.toList(matchItem.getValue())));
                            }
                        } else {
                            throw new GlobalException("使用notin子句时匹配值必须为数组");
                        }
                    }
                    case IS_NULL -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.isNull(fieldName));
                        } else {
                            queryWrapper.or(qw -> qw.isNull(fieldName));
                        }
                    }
                    case IS_NOT_NULL -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.isNotNull(fieldName));
                        } else {
                            queryWrapper.or(qw -> qw.isNotNull(fieldName));
                        }
                    }
                    case BETWEEN -> {
                        if (matchItem.getValue() instanceof List) {
                            List<?> objects = Convert.toList(matchItem.getValue());
                            if (objects.size() < 2) {
                                throw new GlobalException("between条件格式不正确，必须以数组形式提交两个边界值");
                            }
                            if (matchItem.getAnd()) {
                                queryWrapper.and(qw -> qw.between(fieldName, objects.get(0), objects.get(1)));
                            } else {
                                queryWrapper.or(qw -> qw.between(fieldName, objects.get(0), objects.get(1)));
                            }
                        } else {
                            throw new GlobalException("between条件格式不正确，必须以数组形式提交两个边界值");
                        }
                    }
                    case NOT_BETWEEN -> {
                        if (matchItem.getValue() instanceof List) {
                            List<?> objects = Convert.toList(matchItem.getValue());
                            if (objects.size() < 2) {
                                throw new GlobalException("notBetween条件格式不正确，必须以数组形式提交两个边界值");
                            }
                            if (matchItem.getAnd()) {
                                queryWrapper.and(qw -> qw.notBetween(fieldName, objects.get(0), objects.get(1)));
                            } else {
                                queryWrapper.or(qw -> qw.notBetween(fieldName, objects.get(0), objects.get(1)));
                            }
                        } else {
                            throw new GlobalException("notBetween条件格式不正确，必须以数组形式提交两个边界值");
                        }
                    }
                    default -> {
                        if (matchItem.getAnd()) {
                            queryWrapper.and(qw -> qw.eq(fieldName, matchItem.getValue()));
                        } else {
                            queryWrapper.or(qw -> qw.eq(fieldName, matchItem.getValue()));
                        }
                    }
                }
            }
        }
        return queryWrapper;
    }

}
